package com.example.servicegateway.authentication;

import com.example.servicegateway.authentication.component.AuthComponent;
import com.example.servicegateway.constants.AuthConstant;
import com.example.servicegateway.entity.TokenInfo;
import com.example.servicegateway.utils.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 鉴权管理器，作用是对请求经过网关的接口进行权限校验
 */
@Slf4j
@Component
public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Resource
    private Map<String, AuthComponent> map;

    /**
     * 接口权限校验，校验角色是否有访问接口的权限
     * @param authentication 鉴权对象
     * @param authorizationContext 鉴权容器
     * @return 校验是否通过对象
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        String path = request.getURI().getPath();
        log.info("准备鉴权url：{}", path);

        // 如果token以"bearer "为前缀，到此方法里说明JWT有效即已认证
        String token = request.getHeaders().getFirst(AuthConstant.JWT_TOKEN_HEADER);
        if (StringUtils.isBlank(token) || !StringUtils.startsWithIgnoreCase(token, AuthConstant.JWT_TOKEN_PREFIX) ) {
            log.info("token为空或token没有带上bearer，当前获取token：{}", token);
            return Mono.just(new AuthorizationDecision(false));
        }

        TokenInfo tokenInfo = TokenUtils.wrapper(token);
        AuthComponent authComponent = map.get(tokenInfo.getAuthModel());
        // 判断JWT中携带的用户角色是否有权限访问
        return authComponent.authentication(tokenInfo, path, authentication, authorizationContext);
    }
}
